<template>
  <div class="root">
    <div class="title">背包问题 -- 01背包的可视化演示</div>
    <div class="menu-bar">

      <select id="country" name="country" @click="checkoutCase($event.target.value)">
        <option :value="dataIndex">案例{{dataIndex}}</option>
        <option :value="index - 1" v-for="index in dataList.length" :key="index">案例{{index -1}}</option>
      </select>
      <el-button class="compute" @click="showMovePath = true;computePath()" type="success">求解路径</el-button>
      <el-button class="start" @click="start" type="danger">开始移动</el-button>
    </div>
    <div class="show-path" v-show="showMovePath">
      <div style="color: #7bafb0">
        田野
      </div>
      <div v-for="houseIndex in thief.movePath" style="color: #e75c37">
         -> 房子{{houseIndex}}
      </div>

    </div>

    <div class="view">
      <div class="house-area" ref="houseList">
        <div class="house" v-for="(good, index) in goods" :ref="`house${index}`">
          <img class="img" src="./assets/img/house.png" alt="">
          <div class="value">价值：{{good.value}}$</div>
          <div class="space">空间：{{good.space}}</div>
          <div class="index">{{index}}号房子</div>
        </div>
      </div>
      <div class="thief" ref="thief" :style="{left: thief.location.left + 'px', top: thief.location.top + 'px'}">
        <img class="img" :src="thief.player" alt="">
        <div class="value">财富：{{thief.view.allValue}}</div>
        <div class="space">容量：{{thief.view.overSpace}}</div>
        <div class="win" :style="{fontSize: thief.winFontSize + 'px'}" v-if="isEnd">YES!!!</div>
      </div>
    </div>
  </div>
</template>

<script>

  import {dataList} from "@/dataList.js";
  import enterAudio from './assets/music/enter.wav';
  import startViewAudio from './assets/music/start.mp3';
  import thiefImage from './assets/img/thief.png';
  import thiefReverseImage from './assets/img/thief-reverse.png';
  import hahaAudio from './assets/music/haha.mp3' ;
  import winAudio from './assets/music/win.wav' ;

  export default {
    data() {
      return {
        dataList: dataList,
        //  选择数据的下标
        dataIndex: 0,
        //  是否显示移动路径
        showMovePath: false,
        //  物品
        goods: dataList[0].goods,
        //  小偷
        thief: {
          //  坐标
          location: {
            left: 1250,
            top: 400
          },
          //  背包
          backpack: [],
          //  背包总空间
          backpackSpace: dataList[0].backpackSpace,
          //  移动状态（enter, quit）
          moveStatus: 'enter',
          //  移动路径（记录这些房子的下标即可）
          movePath: [],
          view: {
            overSpace: dataList[0].backpackSpace, //  背包剩余空间
            allValue: 0   //  背包总财富
          },
          player: thiefImage,
          winFontSize: 16,
        },
        isStart: false,
        isEnd: false,

        dialogVisible: false,
        addCase: {
          goods: [],
          backpackSpace: ''
        }
      }
    },
    methods: {
      init() {
        //  数据初始化
        this.goods = dataList[this.dataIndex].goods;
        this.thief.backpackSpace = dataList[this.dataIndex].backpackSpace
        this.thief.location = {
          left: 220 * (this.goods.length) - 50,
          top: 400
        }
        this.thief.backpack = []
        this.thief.moveStatus = 'enter'
        this.thief.movePath = [];
        this.thief.view = {
          overSpace: this.thief.backpackSpace,
          allValue: 0
        };
        this.thief.player = thiefImage
        this.thief.winFontSize = 16;
        this.isStart = false;
        this.isEnd = false;

        //  将所有房子的背景颜色去除
        for (let i = 0; i < this.goods.length; i++) {
          this.$refs[`house${i}`][0].style.backgroundColor = 'rgb(255, 0, 0, 0)'
        }
      },
      //  开始演示
      start() {
        // 如果正在演示中，则等待演示完毕才能重新演示
        if (this.isStart) {
          return
        }
        //  数据初始化
        this.init()
        //  如果移动路径为空，那么计算移动路径
        if (this.thief.movePath === null || this.thief.movePath.length === 0) {
          this.computePath()
        }
        //  开始演示
        this.isStart = true
        //  播放开始音乐
        this.playMusic(startViewAudio)
        let pathIndex = 0
        let nextHouse = this.thief.movePath[pathIndex]
        //  获取房子中心点下标
        let nextHouseCenterLocation = this.getHouseCenterLocation(nextHouse)
        //  是否播放了哈哈音效
        let isPlayHaha= false

        const timeId = setInterval(() => {
          //  如果当前位置在房子内部，则将房子的背景颜色设置为粉色
          if (this.inHouse(this.thief.location, nextHouse)) {
            console.log(`进入房子${nextHouse}内部`)
            this.$refs[`house${nextHouse}`][0].style.backgroundColor = 'pink'
          }
          //  进入房间的状态
          if (this.thief.moveStatus === 'enter') {
            //  判断当前位置是否到达目标房子中心
            if (this.thief.location.top <= nextHouseCenterLocation.top || this.thief.location.left <= nextHouseCenterLocation.left) {
              //  播放吃金币音效
              this.playMusic(enterAudio)
              //  修改小偷的移动状态为quit
              this.thief.moveStatus = 'quit'
              //  开始偷取物品，财富增加，空间减少
              this.thief.view.allValue += this.goods[nextHouse].value
              this.thief.view.overSpace -= this.goods[nextHouse].space
            } else {
              //  y = kx + b
              //  根据每次x移动的距离，求出y即可
              let k = (this.thief.location.top - nextHouseCenterLocation.top) / (this.thief.location.left - nextHouseCenterLocation.left);
              let b = this.thief.location.top - (this.thief.location.left * k)
              //  小偷正朝向房子的方向前进
              //    向左移动一个单位
              this.thief.location.left -= 7
              //    向上按比例移动
              this.thief.location.top -= k * 7
            }
          }
          //  离开房间的移动状态
          else if (this.thief.moveStatus === 'quit'){
            //  如果小偷top<400，则一直向下移动
            if (this.thief.location.top < 400) {
              this.thief.location.top += 13
            } else {
              //  小偷已经出来，接下来就是偷下一个房间
              if (pathIndex + 1 < this.thief.movePath.length) {
                //  小偷去下一个房间
                pathIndex++
                nextHouse = this.thief.movePath[pathIndex]
                nextHouseCenterLocation = this.getHouseCenterLocation(nextHouse)
                //  将状态改为进入房间的状态
                this.thief.moveStatus = 'enter'
              }
              //  没有可以偷的房间了，能偷的都偷完了
              else {
                //  偷完东西赶紧走
                this.thief.player = thiefReverseImage   //  切换成逃走的背影
                //  播放偷东西后的开心音效
                if (!isPlayHaha) {
                  this.playMusic(hahaAudio)
                  isPlayHaha = true
                }
                if (this.thief.location.left < (220 * (this.goods.length) - 100)) {
                  this.thief.location.left += 20
                } else {
                  //  演示结束
                  this.isStart = false
                  this.showWin()
                  clearInterval(timeId)
                }
              }
            }
          }

        }, 50)
      },
      //  计算移动路径
      computePath() {
        //  先设置固定移动路径，方便测试
        // this.thief.movePath = [ 5, 3 , 0]


        //  物品数量
        let goodsCount = this.goods.length;
        //  背包总空间
        let backpackSpace = this.thief.backpackSpace;
        //  创建背包，i代表物品,j代表背包容量
        this.thief.backpack = []
        //  创建背包标记，代表是否装入该物品
        let isEnter = []

        for (let i = 0; i < goodsCount; i++) {
          this.thief.backpack.push([])
          isEnter.push([])
          for (let j = 0; j < backpackSpace + 1; j++) {
            this.thief.backpack[i].push(0)
            isEnter[i].push(false)
          }
        }

        //  背包是否装入物品0
        for (let j = this.goods[0].space; j < backpackSpace + 1; j++) {
          this.thief.backpack[0][j] = this.goods[0].value;
          isEnter[0][j] = true
        }

        for (let i = 1; i < goodsCount; i++) {
          for (let j = 0; j < backpackSpace + 1; j++) {
            //  判断当前背包是否装入当前物品
            //  如果装入了当前物品，背包的容量剩余 j - goodsSpace[i]，
            //          那么背包中物品的价值为 backpack[i][j] = backpack[i][j - goodsSpace[i]] + goodsValue[i]
            //  如果不装入当前物品，那么价值为   backpack[i][j] = backpack[i][j - 1]
            //      二者情况取价值最大的即可
            if (j >= this.goods[i].space) {
              let enterValue = this.thief.backpack[i - 1][j - this.goods[i].space] + this.goods[i].value;
              let noEnterValue = this.thief.backpack[i - 1][j];
              if (enterValue > noEnterValue) {
                //  装入当前商品
                this.thief.backpack[i][j] = enterValue
                isEnter[i][j] = true
              } else {
                //  不装入当前商品
                this.thief.backpack[i][j] = noEnterValue
              }
            } else {
              //  不装入当前商品
              this.thief.backpack[i][j] = this.thief.backpack[i - 1][j];
            }
          }
        }
        //  寻找偷取房间最大价值的路径
        const movePath = []
        let path = this.goods.length - 1
        let space = backpackSpace
        while (path >= 0) {
          //  判断当前房间是否偷取
          if (isEnter[path][space]) {
            //  记录偷取路径
            movePath.push(path)
            //  来到下一个偷取地点
            space = space - this.goods[path].space
            path--
          } else {
            path--
          }
        }
        this.thief.movePath = movePath

        // console.log('偷取路径：', movePath)
        // console.log('背包：', this.thief.backpack)
        // console.log('偷入标记：', isEnter)
      },
      //  获取房子中心点坐标
      getHouseCenterLocation(houseIndex) {
        //  房子左侧20的间隙
        let leftGap = 20
        //  房子上边间隙为10
        let topGap = 10
        //  房子宽度
        let houseWidth = 200
        //  房子高度
        let houseHeight = 250

        return {
          left: (leftGap + houseWidth) * (houseIndex + 1) - (60 + houseWidth / 2),
          top: topGap + houseHeight / 2 - 50
        }
      },
      playMusic(musicPath) {
        const audio = new Audio(musicPath)
        audio.play()
      },
      //  偷取完毕，游戏胜利
      showWin() {
        this.isEnd = true
        //  播放胜利音乐
        this.playMusic(winAudio)
        let timeId = setInterval(() => {
          if (this.thief.winFontSize < 56) {
            this.thief.winFontSize += 4
          } else {
            clearInterval(timeId)
          }
        }, 100)
      },
      //  判断当前坐标是否在这个房子内部
      inHouse(location, houseIndex) {
        let houseTop = 10;
        let houseBottom = houseTop + 250;
        let houseLeft = (20 + 200) * (houseIndex + 1) - 200;
        let houseRight = houseLeft + 200;
        return location.left > houseLeft && (location.left < houseRight - 90) && (location.top < houseBottom - 123) && location.top > houseTop;
      },
      checkoutCase(index) {
        console.log(`切换到案例${index}`)
        this.dataIndex = index
      },
    },
    watch: {
      dataIndex (newValue, oldValue) {
        this.init()
        this.showMovePath = false
      }
    }
  }
</script>

<style lang="scss" scoped>
  * {
    box-sizing: border-box;
  }
  .root {
    height: 700px;
    margin: 0 auto;
    padding: 10px 30px;
    //background-color: #ebaf63;
    .title {
      text-align: center;
      color: cadetblue;
      font-size: 36px;
      letter-spacing: 0.2em;
      margin: 10px 0 0 0;
    }
    .menu-bar {
      margin: 30px auto;
      width: 400px;
      display: flex;
      gap: 10px;
      .start {

      }
    }
    .show-path {
      display: flex;
      font-weight: bold;
      font-size: 36px;
      margin-bottom: 30px;
      padding-left: 35%;
    }
    .view {
      overflow-x: auto;
      border: 1px solid black;
      border-radius: 40px;
      position: relative;
      margin: 0 auto;
      min-height: 600px;
      background-color: #7bafb0;
      text-align: center;
      font-size: 28px;
      height: 500px;

      .house-area {
        position: relative;
        display: flex;
        gap: 20px;
        padding: 10px 0 10px 20px;
        .house {
          position: relative;
          min-width: 200px;
          width: 200px;
          height: 250px;
          object-fit: cover;
          font-size: 20px;
          .img {
            width: 100%;
            height: 100%;

          }
          .value {
            left: 5%;
            color: red;
            position: absolute;
            top: 0;
          }
          .space {
            color: green;
            position: absolute;
            right: 5%;
            top: 0;
          }
          .index {
            font-size: 26px;
            font-weight: bolder;
            letter-spacing: 0.1em;
          }
        }
      }
      .thief{
        position: absolute;
        bottom: 0;
        width: 110px;
        height: 153px;
        object-fit: cover;
        .img {
          width: 100%;
          height: 100%;
        }
        .space {
          position: absolute;
          top: 10%;
          right: -50%;
          font-size: 20px;

          color: green;
        }
        .value {
          position: absolute;
          top: -10%;
          right: -50%;
          font-size: 20px;
          color: red;
        }
        .win {
          position: absolute;
          top: -50%;
          color: #ee6542;
          //font-size: 32px;
          letter-spacing: 0.2rem;
          text-align: center;
          width: 100%;
        }
      }
    }
    .dialog {
      .form-item {
        display: flex;
        align-items: center;
        gap: 20px;
        .input {
          width: 20%;
        }

      }
    }
  }
</style>
